<script setup lang="ts">
import {useDrag, useDrop, type XYCoord} from 'vue3-dnd'
import {toRefs} from '@vueuse/core'

interface Props {
  id: any
  index: number
  moveItem: (dragIndex: number, hoverIndex: number) => void
}

const props = defineProps<Props>()
const itemType = 'card'
const itemRef = ref<HTMLDivElement>();
const [dropCollect, drop] = useDrop({
  accept: itemType,
  collect(monitor) {
    return {
      handlerId: monitor.getHandlerId(),
    }
  },
  hover(item, monitor) {
    if (!itemRef.value) {
      return
    }
    const dragIndex = item.index
    const hoverIndex = props.index
    // Don't replace items with themselves
    if (dragIndex === hoverIndex) {
      return
    }

    // Determine rectangle on screen
    const hoverBoundingRect = itemRef.value?.getBoundingClientRect()

    // Get vertical middle
    const hoverMiddleY = (hoverBoundingRect.bottom - hoverBoundingRect.top) / 2

    // Determine mouse position
    const clientOffset = monitor.getClientOffset()

    // Get pixels to the top
    const hoverClientY = (clientOffset as XYCoord).y - hoverBoundingRect.top

    // Only perform the move when the mouse has crossed half of the items height
    // When dragging downwards, only move when the cursor is below 50%
    // When dragging upwards, only move when the cursor is above 50%

    // Dragging downwards
    if (dragIndex < hoverIndex && hoverClientY < hoverMiddleY) {
      return
    }

    // Dragging upwards
    if (dragIndex > hoverIndex && hoverClientY > hoverMiddleY) {
      return
    }

    // Time to actually perform the action
    props.moveItem(dragIndex, hoverIndex)

    // Note: we're mutating the monitor item here!
    // Generally it's better to avoid mutations,
    // but it's good here for the sake of performance
    // to avoid expensive index searches.
    item.index = hoverIndex
  },
})

const [collect, drag, preview] = useDrag({
  type: itemType,
  item: () => {
    return {id: props.id, index: props.index}
  },
  collect: (monitor: any) => ({
    isDragging: monitor.isDragging(),
  }),
})

const {handlerId} = toRefs(dropCollect)
const {isDragging} = toRefs(collect)
const opacity = computed(() => (unref(isDragging) ? 0 : 1))

const setRef = (el: HTMLDivElement) => {
  itemRef.value = preview(drop(el)) as HTMLDivElement
}
</script>
<template>
  <div
      :ref="setRef"
      class="reorderable-item"
      :style="{ opacity }"
      :data-handler-id="handlerId"
      select-none
  >
    <n-space justify="space-between" align="center">
      <slot></slot>
      <div cursor-move :ref="drag">
        <n-icon size="24" align-mid>
          <icon-lucide:grip/>
        </n-icon>
      </div>
    </n-space>
  </div>
</template>
<style scoped>
.reorderable-item {
  margin-bottom: 0.5rem;
  padding: 0.5rem 1rem;
  border: 1px dashed gray;
}

</style>
